﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using Nox.Amuse.Helpers;
using Nox.Amuse.Model;
using Nox.Amuse.MVVM;
using System.Windows.Controls.Primitives;
using System.Collections.ObjectModel;
using System.Collections.Generic;

namespace Nox.Amuse.ViewModel
{
    public class TabViewModel : ObservableObject
    {
        private WorldData myWorldDataModel;

        private Session mySession;


        public string DisplayName { get { return myWorldDataModel.Name; } }

        public string Address { get { return myWorldDataModel.Address; } }

        public string Port { get { return myWorldDataModel.Port.ToString(); } }

        public SessionStatus SessionStatus
        {
            get { return mySession.Status; }
        }


        private FlowDocument myOutput;
        public FlowDocument Output
        {
            get { return myOutput; }
        }

        private double myOutputScrollPosition;
        public double OutputScrollPosition
        {
            get { return myOutputScrollPosition; }
            set
            {
                myOutputScrollPosition = value;
                System.Diagnostics.Debug.WriteLine(this.DisplayName + " - Scroll Position setting to :" + value.ToString());
                base.RaisePropertyChanged("OutputScrollPosition");
            }
        }

        /// <summary>
        /// Forces the OutputScroll back to the bottom just in case the box size changed while we weren't active
        /// </summary>
        private void PositionCheck()
        {
            System.Diagnostics.Debug.WriteLine(this.DisplayName + " PositionCheck - " + myIsOutputAtBottom.ToString());
            if (myIsOutputAtBottom)
                OutputScrollPosition = double.MaxValue;
        }

        private bool myIsOutputAtBottom = true;
        public bool? IsOutputAtBottom
        {
            get { return myIsOutputAtBottom; }
            set
            {
                if (value.HasValue)
                {
                    myIsOutputAtBottom = value.Value;
                    System.Diagnostics.Debug.WriteLine(this.DisplayName + " - Output setting to :" + value.ToString());

                    base.RaisePropertyChanged("IsOutputAtBottom");
                }
                if (myIsOutputAtBottom)
                    if (MoreContentAlertVisibility != Visibility.Collapsed)
                        MoreContentAlertVisibility = Visibility.Collapsed;
            }
        }

        private Visibility myMoreContentAlertVisibility = Visibility.Collapsed;
        public Visibility MoreContentAlertVisibility
        {
            get { return myMoreContentAlertVisibility; }
            set
            {
                myMoreContentAlertVisibility = value;
                base.RaisePropertyChanged("MoreContentAlertVisibility");
            }
        }

        private bool myShowHistory;
        public bool ShowHistory
        {
            get { return myShowHistory; }
            set
            {
                if (myShowHistory != value)
                {
                    myShowHistory = value;
                    if (myShowHistory)
                        this.InputHeight = new GridLength(this.InputHeight.Value + 80);
                    else
                        this.InputHeight = new GridLength(this.InputHeight.Value - 80);
                    this.RaisePropertyChanged("ShowHistory");
                }
            }
        }

        private CommandHistoryViewModel myHistoryVM;
        public CommandHistoryViewModel CommandHistoryViewModel
        {
            get { return myHistoryVM; }
            set
            {
                myHistoryVM = value;
                this.RaisePropertyChanged("CommandHistoryViewModel");
            }
        }
        void CommandHistoryViewModel_ExternalHistoryWindowOpened(object sender, EventArgs e)
        {
            ShowHistory = false;
        }
        void CommandHistoryViewModel_CommandRecalled(object sender, EventArgs e)
        {
            Input = CommandHistoryViewModel.SelectedHistoryItem.OriginalString;
        }

        public GridLength InputHeight
        {
            get { return (myWorldDataModel.InputHeight == 0) ? new GridLength(60) : new GridLength(myWorldDataModel.InputHeight); }
            set
            {
                myWorldDataModel.InputHeight = value.Value;
                base.RaisePropertyChanged("InputHeight");
            }
        }


        private string myInput;
        public string Input
        {
            get { return myInput; }
            set
            {
                myInput = value;
                base.RaisePropertyChanged("Input");
            }
        }


        public TabViewModel(WorldData worldData)
        {
            App.Messenger.Register<TabViewModel>(Messages.TabChanged, (vm) => { if (vm == this) PositionCheck(); });
            myWorldDataModel = worldData;

            myOutput = new FlowDocument(new Paragraph());
            myOutput.Background = Brushes.Black;
            myOutput.FontFamily = new FontFamily("Consolas");
            myOutput.FontSize = 14;
            myOutput.PageWidth = 700;
            myOutput.TextAlignment = TextAlignment.Left;

            Send = new RoutedUICommand();
            Send.InputGestures.Add(new KeyGesture(Key.Enter));
        }



        public void Connect()
        {
            if (mySession != null) // Unregister any left over handlers
            {
                mySession.LineReceived -= new EventHandler<LineReceivedEventArgs>(SessionLineReceived);
                mySession.StatusChanged -= new EventHandler(mySession_StatusChanged);
            }
            mySession = new Session(myWorldDataModel);
            mySession.LineReceived += new EventHandler<LineReceivedEventArgs>(SessionLineReceived);
            mySession.StatusChanged += new EventHandler(mySession_StatusChanged);
            mySession.StartConnectionAsync();

            if (CommandHistoryViewModel == null)
            {
                CommandHistoryViewModel = new CommandHistoryViewModel();
                CommandHistoryViewModel.CommandRecalled += new EventHandler(CommandHistoryViewModel_CommandRecalled);
                CommandHistoryViewModel.ExternalHistoryWindowOpened += new EventHandler(CommandHistoryViewModel_ExternalHistoryWindowOpened);
            }
            CommandHistoryViewModel.UpdateSession(mySession);
        }

        public void Disconnect()
        {
            if (mySession.Status != Model.SessionStatus.Disconnected) // Don't bother if we've already disconnected
            {
                mySession.Disconnect();
            }
        }

        public void Close()
        {
            if (mySession.Status != Model.SessionStatus.Disconnected) // Don't bother if we've already disconnected
            {
                mySession.Disconnect();
            }

            // Save any data and so forth here

            App.Messenger.NotifyColleagues(Messages.TabRemoveRequest, this);
        }

        void mySession_StatusChanged(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// Handles a LineRecieved event from the Session data model and adds it to the OutputDocument
        /// </summary>
        /// <param name="sender">Object that sent the event</param>
        /// <param name="e">Arguments relating to the event</param>
        void SessionLineReceived(object sender, LineReceivedEventArgs e)
        {
            var newInlines = LineProcessor.FormatLine(mySession.GetLine(e.LineGuid));

            var paragraph = myOutput.Blocks.FirstBlock as Paragraph;
            paragraph.Inlines.Add(new LineBreak());
            paragraph.Inlines.AddRange(newInlines);

            if (myIsOutputAtBottom)
                OutputScrollPosition = double.MaxValue;
            else
                MoreContentAlertVisibility = Visibility.Visible;

            App.Messenger.NotifyColleagues(Messages.LineAdded);
        }

        // Send Command
        /// <summary>
        /// Command definition for sending text to the server
        /// </summary>
        public RoutedUICommand Send { get; private set; }
        /// <summary>
        /// Performs the Send Command
        /// </summary>
        /// <param name="sender">Object that sent the command</param>
        /// <param name="e">Arguments relating to the command</param>
        public void SendExecute(object sender, ExecutedRoutedEventArgs e)
        {
            mySession.SendCommand(Input);
            myHistoryVM.RecordCommand(new CommandHistoryItem(Input));
            Input = string.Empty;
        }
        /// <summary>
        /// Checks if the Send Command can be performed
        /// </summary>
        /// <param name="sender">Object that sent the command</param>
        /// <param name="e">Arguments relating to the command</param>
        public void CanSendExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (mySession.Status == SessionStatus.Connected && !string.IsNullOrEmpty(Input))
            {
                e.CanExecute = true;
            }
            e.Handled = true;
        }

        public ICommand ToggleHistoryStateCommand
        {
            get
            {
                return myToggleHistoryStateCommand ?? (myToggleHistoryStateCommand = new RelayCommand(
                    () =>
                    {
                        ShowHistory = !ShowHistory;
                    }
                    ));
            }
        }
        private RelayCommand myToggleHistoryStateCommand;

        public ICommand ShowExternalHistoryCommand
        {
            get
            {
                return myShowExternalHistoryCommand ?? (myShowExternalHistoryCommand = new RelayCommand(
                    () =>
                    {
                        myHistoryVM.OpenHistoryWindowCommand.Execute(null);
                    }
                    ));
            }
        }
        private RelayCommand myShowExternalHistoryCommand;

        public ICommand SendLastCommandCommand
        {
            get
            {
                return mySendLastCommandCommand ?? (mySendLastCommandCommand = new RelayCommand(
                    () =>
                    {
                        myHistoryVM.SelectedHistoryItem = myHistoryVM.CommandHistoryItems.Last();
                        myHistoryVM.SendCommand.Execute(null);
                    },
                    () =>
                    {
                        return myHistoryVM.CommandHistoryItems.Count > 0 && this.SessionStatus == Model.SessionStatus.Connected;
                    }
                    ));
            }
        }
        private RelayCommand mySendLastCommandCommand;

        public ICommand RecallPrevCommandCommand
        {
            get
            {
                return myRecallPrevCommandCommand ?? (myRecallPrevCommandCommand = new RelayCommand(
                    () =>
                    {
                        if (myHistoryVM.SelectedHistoryIndex <= 0 && myHistoryVM.CommandHistoryItems.Count > 1)
                            myHistoryVM.SelectedHistoryIndex = myHistoryVM.CommandHistoryItems.Count - 1;
                        if (Input != myHistoryVM.SelectedHistoryItem.OriginalString)
                            Input = myHistoryVM.SelectedHistoryItem.OriginalString;
                        else
                        {
                            myHistoryVM.SelectedHistoryIndex--;
                            if (myHistoryVM.SelectedHistoryIndex > -1)
                                Input = myHistoryVM.SelectedHistoryItem.OriginalString;
                        }
                    },
                    () =>
                    {
                        return myHistoryVM.CommandHistoryItems.Count > 0;
                    }
                    ));
            }
        }
        private RelayCommand myRecallPrevCommandCommand;

        public ICommand RecallNextCommandCommand
        {
            get
            {
                return myRecallNextCommandCommand ?? (myRecallNextCommandCommand = new RelayCommand(
                    () =>
                    {
                        if (myHistoryVM.SelectedHistoryIndex == myHistoryVM.CommandHistoryItems.Count - 1)
                            myHistoryVM.SelectedHistoryIndex = 0;
                        else
                            myHistoryVM.SelectedHistoryIndex++;
                        Input = myHistoryVM.SelectedHistoryItem.OriginalString;
                    },
                    () =>
                    {
                        return myHistoryVM.CommandHistoryItems.Count > 0;
                    }
                    ));
            }
        }
        private RelayCommand myRecallNextCommandCommand;

    }
}
